#include "xcbcapture.h"
extern "C" {
#include <libavutil/error.h>
}
#include <xcb/xcb.h>
#include <X11/Xlib.h>
#include <QKeySequence>

XcbCapture::XcbCapture(int /*winId*/, QObject *parent) : ScreenCapture(parent), img(nullptr)
{
    const xcb_setup_t *setup;
        int screen_num;
    conn = xcb_connect(nullptr, &screen_num);
    setup = xcb_get_setup(conn);
    screen = get_screen(setup, screen_num);
    xcb_get_geometry_cookie_t gc  = xcb_get_geometry(conn, screen->root);
    xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(conn, gc, nullptr);
    m_width = geo->width;
    m_height = geo->height;
    m_x = geo->x;
    m_y = geo->y;
    display = XOpenDisplay(nullptr);
    // TODO:应该对display进行nullptr判定。
    xdo = xdo_new(reinterpret_cast<char*>(display));
}

XcbCapture::~XcbCapture()
{
    xdo_free(xdo);
}

void XcbCapture::updateFramebuffer()
{
    xcb_drawable_t drawable = screen->root;
    xcb_generic_error_t *e = nullptr;
    xcb_get_image_cookie_t iq = xcb_get_image(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, drawable, 0, 0, 1920, 1080, ~0u);//请求图像
    if(img)
    {
        free(img);
    }
    img = xcb_get_image_reply(conn, iq, &e);//获取图像token
}

int XcbCapture::getFramebufferSize()
{
    return xcb_get_image_data_length(img); //图像长度
}

QByteArray XcbCapture::getFramebuffer()
{
    int length = XcbCapture::getFramebufferSize();
    QByteArray data(length, 0);
    getFramebuffer(data.data());
    return data;
}

void XcbCapture::getFramebuffer(char* data)
{
    int length = XcbCapture::getFramebufferSize();
    uint8_t *img_data = xcb_get_image_data(img); //获取图像数据

    memcpy(data, img_data, static_cast<size_t>(length));
}

AVPixelFormat XcbCapture::pixelFormat()
{
    const xcb_setup_t *setup = xcb_get_setup(conn);
    const xcb_format_t *fmt  = xcb_setup_pixmap_formats(setup);
    int length               = xcb_setup_pixmap_formats_length(setup);

    xcb_get_geometry_cookie_t gc  = xcb_get_geometry(conn, screen->root);
    xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(conn, gc, nullptr);

    AVPixelFormat pix_fmt = AV_PIX_FMT_NONE;

    while (length--) {
        if (fmt->depth == geo->depth) {
            switch (geo->depth) {
            case 32:
                if (fmt->bits_per_pixel == 32)
                    pix_fmt = AV_PIX_FMT_0RGB;
                break;
            case 24:
                if (fmt->bits_per_pixel == 32)
                    pix_fmt = AV_PIX_FMT_0RGB32;
                else if (fmt->bits_per_pixel == 24)
                    pix_fmt = AV_PIX_FMT_RGB24;
                break;
            case 16:
                if (fmt->bits_per_pixel == 16)
                    pix_fmt = AV_PIX_FMT_RGB565;
                break;
            case 15:
                if (fmt->bits_per_pixel == 16)
                    pix_fmt = AV_PIX_FMT_RGB555;
                break;
            case 8:
                if (fmt->bits_per_pixel == 8)
                    pix_fmt = AV_PIX_FMT_RGB8;
                break;
            }
        }

        if (~pix_fmt) {
            return pix_fmt;
        }

        fmt++;
    }

    return pix_fmt;
}

xcb_screen_t* XcbCapture::get_screen(const xcb_setup_t *setup, int screen_num)
{
        xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup);
        xcb_screen_t *screen     = nullptr;

        for (; it.rem > 0; xcb_screen_next (&it)) {
            if (!screen_num) {
                screen = it.data;
                break;
            }

            screen_num--;
        }

        return screen;
}

int XcbCapture::winId()
{
    return 0;
}

void XcbCapture::postMouseEvent(int button, bool isPress, int x,int y)
{
    Qt::MouseButtons btn = Qt::MouseButtons(button);
    xdo_move_mouse(xdo, x, y, 0);
    if(btn.testFlag(Qt::MouseButton::LeftButton))
    {
        if(isPress)
        {
            xdo_mouse_down(xdo, CURRENTWINDOW, Button1);
        }
        else
        {
            xdo_mouse_up(xdo, CURRENTWINDOW, Button1);
        }
    }
    if(btn.testFlag(Qt::MouseButton::RightButton))
    {
        if(isPress)
        {
            xdo_mouse_down(xdo, CURRENTWINDOW, Button3);
        }
        else
        {
            xdo_mouse_up(xdo, CURRENTWINDOW, Button3);
        }
    }
}

void XcbCapture::postKeyEvent(int key, bool isPress)
{
    if(isPress)
    {
        xdo_send_keysequence_window_down(xdo, CURRENTWINDOW, translateKeyCode(key), 0);
    }
    else
    {
        xdo_send_keysequence_window_up(xdo, CURRENTWINDOW, translateKeyCode(key), 0);
    }
}

const char* XcbCapture::translateKeyCode(int key)
{
    Qt::Key k = Qt::Key(key);
    QString ret="";
    QKeySequence seq(k);



    switch(key)
    {
    case Qt::Key_Shift:
        ret="Shift";
        break;
    case Qt::Key_Control:
        ret="Control";
        break;
    case Qt::Key_Alt:
        ret="Alt";
        break;
    case Qt::Key_Meta:
        ret="Meta";
        break;
    case Qt::Key_CapsLock:
        ret="Caps_Lock";
        break;
    case Qt::Key_Space:
        ret="space";
        break;
    case Qt::Key_PageUp:
        ret="Page_Up";
        break;
    case Qt::Key_PageDown:
        ret="Page_Down";
        break;
    case Qt::Key_Backspace:
        ret="BackSpace";
        break;
    case Qt::Key_Exclam:
        ret="exclam";
        break;
    case Qt::Key_QuoteDbl:
        ret="quotedbl";
        break;
    case Qt::Key_NumberSign:
        ret="numbersign";
        break;
    case Qt::Key_Dollar:
        ret = "dollar";
        break;
    case Qt::Key_Percent:
        ret = "percent";
        break;
    case Qt::Key_Ampersand:
        ret = "ampersand";
        break;
    case Qt::Key_Apostrophe:
        ret = "apostrophe";
        break;
    case Qt::Key_QuoteLeft:
        ret = "quoteright";
        break;
    case Qt::Key_ParenLeft:
        ret = "parenleft";
        break;
    case Qt::Key_ParenRight:
        ret = "parenright";
        break;
    case Qt::Key_Asterisk:
        ret = "asterisk";
        break;
    case Qt::Key_Plus:
        ret = "plus";
        break;
    case Qt::Key_Comma:
        ret = "comma";
        break;
    case Qt::Key_Minus:
        ret = "minus";
        break;
    case Qt::Key_Period:
        ret = "period";
        break;
    case Qt::Key_Slash:
        ret = "slash";
        break;
    case Qt::Key_Colon:
        ret = "colon";
        break;
    case Qt::Key_Semicolon:
        ret = "semicolon";
        break;
    case Qt::Key_Less:
        ret = "less";
        break;
    case Qt::Key_Equal:
        ret = "equal";
        break;
    case Qt::Key_Greater:
        ret = "greater";
        break;
    case Qt::Key_Question:
        ret = "question";
        break;
    case Qt::Key_At:
        ret = "at";
        break;

    case Qt::Key_A:
    case Qt::Key_B:
    case Qt::Key_C:
    case Qt::Key_D:
    case Qt::Key_E:
    case Qt::Key_F:
    case Qt::Key_G:
    case Qt::Key_H:
    case Qt::Key_I:
    case Qt::Key_J:
    case Qt::Key_K:
    case Qt::Key_L:
    case Qt::Key_M:
    case Qt::Key_N:
    case Qt::Key_O:
    case Qt::Key_P:
    case Qt::Key_Q:
    case Qt::Key_R:
    case Qt::Key_S:
    case Qt::Key_T:
    case Qt::Key_U:
    case Qt::Key_V:
    case Qt::Key_W:
    case Qt::Key_X:
    case Qt::Key_Y:
    case Qt::Key_Z:
        ret = seq.toString(QKeySequence::SequenceFormat::NativeText).toLower();
        break;
    default:
        ret = seq.toString(QKeySequence::SequenceFormat::NativeText);
        break;
    }
    qDebug()<<"@@@"<<ret<<"###"<<QString::number(key, 16)<<"$$$";

    return ret.toLocal8Bit();
}
